package com.rao.mideng.core;

import com.rao.mideng.annotation.RaoLogAnno;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;

public class LogBeanPostProcessor implements MergedBeanDefinitionPostProcessor {

    private LogAnnoAdvisor advisor;

    /**
     * @param bean
     * @param beanName
     * @return 给bean追加log切面
     * @throws BeansException
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Advised) {
            Advised advised = (Advised) bean;
            Class<?> targetClass = advised.getTargetClass();

            //找有没有@RaoMiDeng，方法上类上是否有这个注解。
            boolean hasLogAnno = classHasRaoMiDengAnno(targetClass);
            if (!hasLogAnno) {
                return bean;
            }

            //spring的初始化一般都是单线程
            if (this.advisor == null) {
                this.advisor = new LogAnnoAdvisor(new LogInterceptor());
            }
            //继续给这个代理对象，添加代理逻辑，放在首位
            advised.addAdvisor(0, advisor);
            return bean;
        }

        boolean hasLogAnno = classHasRaoMiDengAnno(bean.getClass());
        if (hasLogAnno) {
            ProxyFactory proxyFactory = new ProxyFactory();
            ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
            if (this.advisor == null) {
                this.advisor = new LogAnnoAdvisor(new LogInterceptor());
            }
            proxyFactory.addAdvisors(advisor);
            proxyFactory.setTargetSource(new SingletonTargetSource(bean));
            return proxyFactory.getProxy(classLoader);
        }
        return bean;
    }

    /**
     * 递归式寻找类上有没有@RaoMiDeng注解。
     */
    private boolean classHasRaoMiDengAnno(Class<?> clazz) {
        //先检索类上是否有注解
        RaoLogAnno annotation = clazz.getAnnotation(RaoLogAnno.class);
        if (annotation != null) {
            return true;
        }
        //再去检索方法，本类上的方法
        if (methodHasRaoMiDengAnno(clazz)) {
            return true;
        } else {
            //探索父类
            Class<?> cls;
            if ((cls = clazz.getSuperclass()) != Object.class) {
                return classHasRaoMiDengAnno(cls);
            } else {
                return false;
            }
        }
    }

    /**
     * clazz.getMethods()获取的是父类的public与子类的public方法，controller的方法含有@RequstMapping都是public
     */
    private boolean methodHasRaoMiDengAnno(Class<?> clazz) {
        /**<? public方法即可*/
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if (null != method.getAnnotation(RaoLogAnno.class)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {

    }
}
